
extern "C"
{
	#include <lualib.h>
	#include <lauxlib.h>
}
#include <string>
#include <sstream>
#include <cctype>
#include <algorithm>
#include "gluafx.h"

#define GLUAFX_ERROR(message) { std::ostringstream ss; ss << message; onError(ss.str()); }
#define GLUAFX_ASSERT(cond, message) { if (!(cond))  { GLUAFX_ERROR(message) } }

namespace gluafx
{
	namespace
	{
		std::string
		getName(lua_State* L)
		{
			lua_pushstring(L, "name");
			lua_gettable(L, -2);
	
			std::string name = lua_tostring(L, -1);
			lua_pop(L, 1);
			return name;
		}

		std::string
		stringToLower(std::string s)
		{
			std::transform(s.begin(), s.end(), s.begin(), std::tolower);
			return s;
		}
	}

	void
	EffectParser::parse(const std::string& filename)
	{
		lua_State* L = lua_open();
		luaopen_debug(L);
		luaopen_base(L);
		if (luaL_dofile(L, "effectsetup.lua"))
		{
			GLUAFX_ERROR(lua_tostring(L, -1));
			return;
		}
		if (luaL_dofile(L, filename.c_str()))
		{
			GLUAFX_ERROR(lua_tostring(L, -1));
			return;
		}

		lua_getglobal(L, "techniques");
		for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1))
		{
			std::string techniqueName = getName(L);

			onBeginTechnique(techniqueName);

			lua_pushstring(L, "passes");
			lua_gettable(L, -2);

			for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1))
			{
				std::string passName = getName(L);

				onBeginPass(passName);

				lua_pushstring(L, "states");
				lua_gettable(L, -2);
				for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1))
				{
					GLUAFX_ASSERT(lua_isstring(L, -2), "Expected string key in pass " << techniqueName << "." << passName << ", found " << lua_typename(L, lua_type(L, -2)));
					std::string stateName = stringToLower(lua_tostring(L, -2));

					if (stateName == "vertexshader")
					{
						GLUAFX_ASSERT(lua_isstring(L, -1), "Expected vertex shader in pass " << techniqueName << "." << passName << ", found " << lua_typename(L, lua_type(L, -1)));
						onVertexShader(lua_tostring(L, -1));
					}
					else if (stateName == "fragmentshader")
					{
						GLUAFX_ASSERT(lua_isstring(L, -1), "Expected fragment shader in pass " << techniqueName << "." << passName << ", found " << lua_typename(L, lua_type(L, -1)));
						onFragmentShader(lua_tostring(L, -1));
					}
					else
					{
						GLUAFX_ERROR("Unknown effect state: " << stateName << " in " << techniqueName << "." << passName);
					}
				}
				onEndPass();

				lua_pop(L, 1); // pop states
			}

			onEndTechnique();
			lua_pop(L, 1); // pop passes
		}
		lua_pop(L, 1); // pop techniques

		lua_close(L);
	}

} // namespace gluafx